package listener

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/sirupsen/logrus"
	"hcy-api/structs/constant"
	"hcy-api/structs/idp"
	"hcy-api/structs/tables"
	"idp/global"
	"idp/repository"
	"net"
	"runtime"
	"strconv"
	"sync"
	"time"
)

///////////////////// TcpServer 代码  /////////////////////////////////

type TcpClient struct {
	Conn net.Conn
}

var TcpSer *TcpServer

type TcpServer struct {
	Ip          string
	Port        int
	ClientMap   map[int64]*TcpClient
	clientMutex sync.Mutex
}

func NewTcpServer(ip string, port int) *TcpServer {
	server := &TcpServer{
		Ip:          ip,
		Port:        port,
		ClientMap:   make(map[int64]*TcpClient),
		clientMutex: sync.Mutex{},
	}
	return server
}

func SendInstruct(clientId int64, code int, cmd string, argus []string) error {
	cliPlu := idp.PluginInstruct{
		Cmd: cmd,
	}
	if len(argus) > 0 {
		cliPlu.Args = argus
	}
	bs, err := json.Marshal(cliPlu)
	if err != nil {
		return err
	}
	inst := idp.ClientInstruct{
		ClientType: constant.IdpClient,
		Code:       code,
		ServeTime:  time.Now().Unix(),
		Body:       string(bs),
	}
	bs, err = Encode(inst)
	if err != nil {
		return err
	}
	if _, ok := TcpSer.ClientMap[clientId]; !ok {
		err = errors.New("设备不在线")
		return err
	}
	_, err = TcpSer.ClientMap[clientId].Conn.Write(bs)
	if err != nil {
		TcpSer.clientOffline(TcpSer.ClientMap[clientId].Conn)
		global.DeleteClientInstructCatch(clientId)
	}
	return err
}

func LoadIdpCatch() {
	var (
		li  []tables.IdpClient
		err error
	)
	if li, err = repository.ClientRepository.FindAll(); err != nil {
		logrus.Error(err)
		return
	}
	for _, item := range li {
		var (
			cpu         tables.IdpLogCpu
			memory      tables.IdpLogMemory
			diskPhysics tables.IdpLogDisk
			boardBase   tables.IdpLogBoard
			sysAbstract tables.IdpLogSysAbstract
		)
		if cpu, err = repository.WinLogRepository.FindLastCpu(item.Id); err != nil {
			logrus.Error(err)
			return
		}
		if memory, err = repository.WinLogRepository.FindLastMemory(item.Id); err != nil {
			logrus.Error(err)
			return
		}
		if diskPhysics, err = repository.WinLogRepository.FindLastDisk(item.Id); err != nil {
			logrus.Error(err)
			return
		}
		if boardBase, err = repository.WinLogRepository.FindLastBoard(item.Id); err != nil {
			logrus.Error(err)
			return
		}
		if sysAbstract, err = repository.WinLogRepository.FindLastSysAbstract(item.Id); err != nil {
			logrus.Error(err)
			return
		}
		cache := global.IdpCache{
			CPU:         cpu,
			Memory:      memory,
			DiskPhysics: diskPhysics,
			BoardBase:   boardBase,
			SysAbstract: sysAbstract,
		}
		global.InfoCatchMaoForIdp.Store(item.Id, &cache)
	}
}

// sendHandsUpInstruct 不要主动通知客户端举手
func sendHandsUpInstruct(conn net.Conn) (err error) {
	inst := idp.ClientInstruct{
		Code:      constant.HandsUp,
		ServeTime: time.Now().Unix(),
	}
	var bs []byte
	bs, err = Encode(inst)
	if err != nil {
		return err
	}
	_, err = conn.Write(bs)
	return
}

func sendInstructForSetDeviceId(clientId int64, conn net.Conn) (err error) {
	inst := idp.ClientInstruct{
		Code:      constant.SetDeviceId,
		ServeTime: time.Now().Unix(),
		Body:      strconv.FormatInt(clientId, 10),
	}
	var bs []byte
	bs, err = Encode(inst)
	if err != nil {
		return err
	}
	_, err = conn.Write(bs)
	return
}

func IsOnline(clientId int64) (b bool) {
	_, b = TcpSer.ClientMap[clientId]
	return b
}

func NewClient(conn net.Conn) *TcpClient {
	client := &TcpClient{
		Conn: conn,
	}
	return client
}

func (s *TcpServer) Start() error {
	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", s.Ip, s.Port))
	defer listener.Close()
	if err != nil {
		logrus.Errorf("启动 tcp 监听服务失败,监听地址为：%s:%d", s.Ip, s.Port)
		return err
	}
	for {
		con, err := listener.Accept()
		if err != nil {
			logrus.Errorf("客户端连接失败：%v", err)
			continue
		}
		// 开启协程，监听客户端消息
		go s.listenMsg(con)
	}
}

// listenMsg 将消息解码还原为 ClientResponse 对象，然后处理消息
func (s *TcpServer) listenMsg(conn net.Conn) {
	for {
		var (
			msg idp.ClientResponse
			bs  []byte
			err error
		)
		bs, err = Decode(conn)
		if err != nil {
			logrus.Error(err)
			s.clientOffline(conn)
			continue
		}
		err = json.Unmarshal(bs, &msg)
		if err != nil {
			logrus.Error(err)
			continue
		}
		s.HandMsg(msg, conn)
	}
}
func (s *TcpServer) clientOnline(clientId int64, conn net.Conn) {
	s.clientMutex.Lock()
	s.ClientMap[clientId] = NewClient(conn)
	s.clientMutex.Unlock()
}

func (s *TcpServer) clientOffline(conn net.Conn) {
	// 移除 map中的连接记录
	s.clientMutex.Lock()
	for key := range s.ClientMap {
		if s.ClientMap[key].Conn == conn {
			delete(s.ClientMap, key)
		}
	}
	s.clientMutex.Unlock()
	// 关闭连接，释放句柄
	_ = conn.Close()
	// 通知协程退出
	runtime.Goexit()
}

func InitTcpServer() {
	TcpSer = NewTcpServer(global.IdpConf.Tcp.Ip, global.IdpConf.Tcp.Port)
	go func() {
		err := TcpSer.Start()
		if err != nil {
			logrus.Errorf("启动tcp服务失败:%s", err)
			return
		}
	}()
	go func() {
		ticker := time.NewTicker(60 * time.Second) //60
		for range ticker.C {
			for _, v := range TcpSer.ClientMap {
				_, err := v.Conn.Write([]byte("ping"))
				if err != nil {
					TcpSer.clientOffline(v.Conn)
				}
			}
		}
	}()

}
